package com.xinshujia.filter.authentication;

import com.xinshujia.config.authentication.token.MyPhoneAuthenticationToken;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.lang.Nullable;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.util.Assert;

/**
 * *************************************
 * *   @author: 野狼上校               **
 * *   @since: 2024-07-16 13:42       **
 * *   @version: 1.0                  **
 * *************************************
 * <p>
 */
public class PhoneAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

 public static final String SPRING_SECURITY_FORM_PHONE_KEY = "phone";

 public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password";

 private static final AntPathRequestMatcher DEFAULT_ANT_PATH_REQUEST_MATCHER = new AntPathRequestMatcher("/login",
         "POST");

 private String phoneParameter = SPRING_SECURITY_FORM_PHONE_KEY;

 private String passwordParameter = SPRING_SECURITY_FORM_PASSWORD_KEY;

 private boolean postOnly = true;

 public PhoneAuthenticationFilter() {
  super(DEFAULT_ANT_PATH_REQUEST_MATCHER);
 }

 public PhoneAuthenticationFilter(AuthenticationManager authenticationManager) {
  super(DEFAULT_ANT_PATH_REQUEST_MATCHER, authenticationManager);
 }

 @Override
 public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
         throws AuthenticationException {
  if (this.postOnly && !request.getMethod().equals("POST")) {
   throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
  }
  String phone = obtainPhone(request);
  phone = (phone != null) ? phone.trim() : "";
  String password = obtainPassword(request);
  password = (password != null) ? password : "";
  MyPhoneAuthenticationToken authRequest = MyPhoneAuthenticationToken.unauthenticated(phone,
          password);
  // Allow subclasses to set the "details" property
  setDetails(request, authRequest);
  return this.getAuthenticationManager().authenticate(authRequest);
 }

 /**
  * Enables subclasses to override the composition of the password, such as by
  * including additional values and a separator.
  * <p>
  * This might be used for example if a postcode/zipcode was required in addition to
  * the password. A delimiter such as a pipe (|) should be used to separate the
  * password and extended value(s). The <code>AuthenticationDao</code> will need to
  * generate the expected password in a corresponding manner.
  * </p>
  * @param request so that request attributes can be retrieved
  * @return the password that will be presented in the <code>Authentication</code>
  * request token to the <code>AuthenticationManager</code>
  */
 @Nullable
 protected String obtainPassword(HttpServletRequest request) {
  return request.getParameter(this.passwordParameter);
 }

 /**
  * Enables subclasses to override the composition of the username, such as by
  * including additional values and a separator.
  * @param request so that request attributes can be retrieved
  * @return the username that will be presented in the <code>Authentication</code>
  * request token to the <code>AuthenticationManager</code>
  */
 @Nullable
 protected String obtainPhone(HttpServletRequest request) {
  return request.getParameter(this.phoneParameter);
 }

 /**
  * Provided so that subclasses may configure what is put into the authentication
  * request's details property.
  * @param request that an authentication request is being created for
  * @param authRequest the authentication request object that should have its details
  * set
  */
 protected void setDetails(HttpServletRequest request, MyPhoneAuthenticationToken authRequest) {
  authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
 }

 /**
  * Sets the parameter name which will be used to obtain the username from the login
  * request.
  * @param phoneParameter the parameter name. Defaults to "phone".
  */
 public void setUsernameParameter(String phoneParameter) {
  Assert.hasText(phoneParameter, "Username parameter must not be empty or null");
  this.phoneParameter = phoneParameter;
 }

 /**
  * Sets the parameter name which will be used to obtain the password from the login
  * request..
  * @param passwordParameter the parameter name. Defaults to "password".
  */
 public void setPasswordParameter(String passwordParameter) {
  Assert.hasText(passwordParameter, "Password parameter must not be empty or null");
  this.passwordParameter = passwordParameter;
 }

 /**
  * Defines whether only HTTP POST requests will be allowed by this filter. If set to
  * true, and an authentication request is received which is not a POST request, an
  * exception will be raised immediately and authentication will not be attempted. The
  * <tt>unsuccessfulAuthentication()</tt> method will be called as if handling a failed
  * authentication.
  * <p>
  * Defaults to <tt>true</tt> but may be overridden by subclasses.
  */
 public void setPostOnly(boolean postOnly) {
  this.postOnly = postOnly;
 }

 public final String getUsernameParameter() {
  return this.passwordParameter;
 }

 public final String getPasswordParameter() {
  return this.passwordParameter;
 }

}